﻿using System;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Linq;
using SellAndBuy.Domain.Core;

namespace SellAndBuy.Infrastructure.Data.Core.Repositories
{
    /// <summary>
    /// NHibernate Repository
    /// </summary>
    /// <typeparam name="TEntity">Entity type</typeparam>
    /// <typeparam name="TID"></typeparam>
    public class Repository<TEntity, TID> : IRepository<TEntity, TID> where TEntity : class, IAggregateRoot
    {
        protected readonly INhibernateUnitOfWork _unitOfWork;

        /// <summary>
        /// Repository Constructor
        /// </summary>
        /// <param name="unitOfWork">Unit of work</param>
        /// <exception cref="ArgumentNullException"><paramref name="unitOfWork" /> is <c>null</c>.</exception>
        protected Repository(INhibernateUnitOfWork unitOfWork)
        {
            if (unitOfWork == null)
                throw new ArgumentNullException("unitOfWork");

            _unitOfWork = unitOfWork;
        }

        /// <summary>
        /// Get the unit of work in this repository
        /// </summary>
        public IUnitOfWork UnitOfWork
        {
            get { return _unitOfWork; }
        }

        /// <summary>
        /// Add an item to repository
        /// </summary>
        /// <param name="item">item to add</param>
        /// <exception cref="ArgumentNullException"><paramref name="item" /> is <c>null</c>.</exception>
        public virtual void Add(TEntity item)
        {
            if (item == null)
                throw new ArgumentNullException("item");

            _unitOfWork.Session.Save(item);
        }

        /// <summary>
        /// Delete item 
        /// </summary>
        /// <param name="item">Item to delete</param>
        /// <exception cref="ArgumentNullException"><paramref name="item" /> is <c>null</c>.</exception>
        public virtual void Remove(TEntity item)
        {
            if (item == null)
                throw new ArgumentNullException("item");

            _unitOfWork.Session.Delete(item);
        }

        /// <summary>
        /// Update entity into the repository
        /// </summary>
        /// <param name="item">Item with changes</param>
        /// <exception cref="ArgumentNullException"><paramref name="item" /> is <c>null</c>.</exception>
        public virtual void Update(TEntity item)
        {
            if (item == null)
                throw new ArgumentNullException("item");

            _unitOfWork.Session.Update(item);
        }

        /// <summary>
        /// Get specific entity by id
        /// </summary>
        /// <param name="id">id</param>
        /// <returns>requested entity</returns>
        public virtual TEntity Find(TID id)
        {
            return _unitOfWork.Session.Get<TEntity>(id);
        }

        /// <summary>
        /// Get specific entity by query (Expression)
        /// </summary>
        /// <param name="query">Expression to evaluate</param>
        /// <returns>Item matching query</returns>
        public virtual TEntity FindBy(Expression<Func<TEntity, bool>> query)
        {
            return _unitOfWork.Session.Query<TEntity>().Where(query).First();
        }
    }
}